有了單元測試,我們對工作的定義範圍就比較注重了,因為多做要多花時間嘛!於是我們就跟主管商量,這兩週就做這些事,如果高層問起進度,就可以以兩週為單位回報,主管發現這樣他也很好做事,而且這群人交出來的東西真的不太會錯,於是每次回報進度也就不用傷腦筋抓 Buffer 了。
敏捷聖徒 Day 1:我不是專家 (Kuma)
你就是不寫測試才會沒時間 2-12 (Kuma)
取決 DB 的管理,若開發者都是共用同一個 DB ,此情況 DB 屬於「外部系統」(視同呼叫第三方的 Api),故不包含測試,如果測試可以使用可以使用 H2 設定,此情況 DB 屬於「內部系統」,則可以單元測試,總的來說,Unit Test 不管怎麼定義,至少「不要牽扯外部系統」
你就是不寫測試才會沒時間 2-2 (Kuma)
單元測試的起手三件套
public class Student {
private String firstName;
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFullName() {
return firstName + " " + lastName;
}
}
class StudentTest {
@Test
void full_name () {
// expect
Student student = new Student("Michael","Jordan");
// actual
String actual = student.getFullName();
// verify
assertEquals("Michael Jordan", actual)
}
}
你就是不寫測試才會沒時間 3-2 (Kuma)
public boolean checkTime(ApplicationForm applicationForm) {
Scholarship scholarship = scholarshipRepository.find(applicationForm.getScholarshipId());
LocalDate deadline = scholarship.getDeadline();
LocalDate today = LocalDate.now();
return today.isEqual(deadline)
|| today.isBefore(deadline);
}
出現以下三個依賴
Application
可以控制,直接 constructor 或 setter,控制 getter 的行為
ScholarshipRepository
無法控制,因為沒有真實 DB,可以使用 Mockito,Mock DB 物件,並指定方法的輸出內容
ScholarshipRepository fakeRepository = Mockito.mock(ScholarshipRepository.class);
Mockito.when(fackRepository.find(777L)).thenReturn(new Scholarship(LocalDate.MAX);
LocalDate.now()
無法控制,因為是 static,會隨著測試的準備階段自動初始方法,無法提前 Mock 取代,不過後來版本的 Mockito 對 static 的支援,因此可以覆蓋,如程式碼舉例
LocalDate expected = LocalDate.of(2029, 12, 31);
Mockito.mockStatic(LocalDate.class).when(LocalDate::now).thenReturn(expected);
治本的方法,就是要意識到目前方法的實作與 static method LocalDate.now() 產生耦合,導致單元測試不容易撰寫,應該重構方法,參考 Dependency Inversion 來解偶,重構如下
public boolean checkTime(Application application, Clock clock) {
LocalDate today = clock.now();
// ...
LocalDate expected = LocalDate.of(2029, 12, 31);
Clock fakeClock = Mockito.mock(Clock.class);
Mockito.when(fackClock.now()).thenReturn(expected);
方法經過重構後,方法原本依賴系統的時間,改成輸入自方法,方法的核心邏輯內聚提高,不受原本 LocalDate.now() 的實作引響
你就是不寫測試才會沒時間 3-5 (Kuma)
「可控的替代物件,用來取代一個外部相依物件」
如果我們先跟假物件串通好與待會要回傳給待測物件值,最後再去檢查待測物件的狀態,那它就是Stub。
「一個假物件,用來驗證待測物件是否如預期般呼叫這個假物件」
如果我們直接讓帶待物件互動,最後再去檢查剛剛假物件與待測物件的互動情形,那它就是 Mock。